package com.ihr360.expr.utils;

import com.ihr360.expr.ExprBoolean;
import com.ihr360.expr.ExprDate;
import com.ihr360.expr.ExprDouble;
import com.ihr360.expr.ExprError;
import com.ihr360.expr.ExprException;
import com.ihr360.expr.ExprInteger;
import com.ihr360.expr.ExprString;
import com.ihr360.expr.context.IEvaluationContext;
import com.ihr360.expr.core.Expr;
import com.ihr360.expr.core.ExprEvaluatable;
import com.ihr360.expr.core.ExprNumber;
import com.ihr360.expr.core.ExprType;
import com.ihr360.expr.parser.ExprLexer;
import com.ihr360.expr.parser.ExprParser;

import java.io.IOException;
import java.util.Date;

public class ExprUtils {

    public static String convertExprAsStringValue(Expr e)
            throws ExprException {
        if (e == null)
            return null;

        if (e instanceof ExprString)
            return ((ExprString) e).str;

        if (e instanceof ExprDouble)
            return ((ExprDouble) e).toString();

        if (e instanceof ExprInteger)
            return ((ExprInteger) e).toString();

        if (e instanceof ExprBoolean)
            return ((ExprBoolean) e).toString();

        if (e instanceof ExprError) {
            return null;
        }

        return e.toString();
    }

    public static Object convertExpr(IEvaluationContext context, Expr e)
            throws ExprException {
        if (e == null)
            return null;

        if (e instanceof ExprEvaluatable)
            e = ((ExprEvaluatable) e).evaluate(context);

        if (e instanceof ExprString)
            return ((ExprString) e).str;

        if (e instanceof ExprDouble)
            return ((ExprDouble) e).doubleValue();

        if (e instanceof ExprInteger)
            return ((ExprInteger) e).intValue();

        if (e instanceof ExprBoolean)
            return ((ExprBoolean) e).booleanValue();

        return e;
    }

    public static Expr[] convertArgs(Object[] args) {
        Expr[] a = new Expr[args.length];
        for (int i = 0; i < args.length; i++) {
            a[i] = ExprUtils.convertObject(args[i]);
        }
        return a;
    }


    public static Expr convertObject(Object o) {
        if (o == null) {
            return null;
        }

        if (o instanceof Double) {
            return new ExprDouble(((Double) o).doubleValue());
        }

        if (o instanceof Integer) {
            return new ExprInteger(((Integer) o).intValue());
        }

        if (o instanceof Boolean) {
            return new ExprBoolean(((Boolean) o).booleanValue());
        }

        if (o instanceof String) {
            return new ExprString((String) o);
        }

        if (o instanceof Date) {
            return new ExprDate((Date) o);
        }

        if (o instanceof Expr) {
            return (Expr) o;
        }
        return null;
    }

    public static Expr parseExpression(String expression) throws ExprException {
        Expr result;
        if (!expression.startsWith("=")) {
            result = ExprUtils.parseValue(expression);
        } else {
            expression = expression.substring(1);
            ExprParser p = new ExprParser();
            try {
                p.parse(new ExprLexer(expression));
            } catch (IOException e) {
                throw new ExprException(e);
            }
            result = p.get();
        }
        return result;
    }

    public static Expr parseValue(String expression) {
        Expr result;
        try {
            result = new ExprInteger(Integer.parseInt(expression));
        } catch (Exception e) {
            try {
                result = new ExprDouble(Double.parseDouble(expression));
            } catch (Exception ex) {
                result = new ExprString(expression);
            }
        }
        return result;
    }


    public static Expr parseValueToDoubleExpr(String expression) {
        Expr result;
        try {
            result = new ExprDouble(Double.parseDouble(expression));
        } catch (Exception ex) {
            result = new ExprString(expression);
        }

        return result;
    }

    //对表达式进行求值运算
    public static Expr evalArg(IEvaluationContext context, Expr arg)
            throws ExprException {
        if (arg instanceof ExprEvaluatable) {
            return ((ExprEvaluatable) arg).evaluate(context);
        }
        return arg;
    }

    public static double asDouble(IEvaluationContext context, Expr arg,
                                  boolean strict) throws ExprException {
        if (arg instanceof ExprEvaluatable) {
            arg = ((ExprEvaluatable) arg).evaluate(context);
        }
        if (arg instanceof ExprNumber) {
            return ((ExprNumber) arg).doubleValue();
        }
        if (!strict) {
            return 0;
        }
        throw new ExprException("Invalid argument type for function ");
    }

    public static String asString(IEvaluationContext context, Expr arg,
                                  boolean strict) throws ExprException {
        if (arg instanceof ExprEvaluatable) {
            arg = ((ExprEvaluatable) arg).evaluate(context);
        }
        if (arg instanceof ExprString) {
            return ((ExprString) arg).str;
        }
        if (!strict) {
            if (arg instanceof ExprNumber) {
                return arg.toString();
            }
            return "";
        }
        throw new ExprException("Invalid argument type for function ");
    }

    public static int compareToNotStrict(IEvaluationContext context, Expr expr, Expr anotherExpr) throws ExprException {
        expr = evalArg(context, expr);
        anotherExpr = evalArg(context, anotherExpr);
        if (expr.type.equals(anotherExpr.type)) {
            if (ExprType.String.equals(expr.type)) {
                return expr.compareIgnoreCase(anotherExpr);
            }
            return expr.compareTo(anotherExpr);
        } else {
            if (expr instanceof ExprNumber && anotherExpr instanceof ExprNumber) {
                return new Double(((ExprNumber) expr).doubleValue()).compareTo(((ExprNumber) anotherExpr).doubleValue());
            }
        }
        String evalExpr = asString(context, expr, false);
        String evalAnohter = asString(context, anotherExpr, false);
        return evalExpr.compareToIgnoreCase(evalAnohter);
    }

}